package com.smartcommunity.elasticsearch.base.impl;

import com.smartcommunity.elasticsearch.base.EsDataService;
import com.smartcommunity.es.common.EsRestUtils;
import com.smartcommunity.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.ActiveShardCount;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 社区es数据操作基类
 * @author 渔七
 * @since 2023年3月9日
 */
@Slf4j
abstract class EsDataBaseService implements EsDataService, InitializingBean {

    @Autowired
    protected EsRestUtils esRestUtils;

    protected final String INDEX_FORMAT = "%s-%s";

    protected final String ALAIS_FORMAT = "%s-alias";

    protected BulkResponse batchInsert(String index, Map<String, String> valueMap, int reTryTime) throws BusinessException {
        try {
            return esRestUtils.createDoc(index, valueMap);
        } catch (Exception e) {
            log.error("数据写入Es异常", e);
            throw new BusinessException("数据写入Es异常");
        }
    }

    private String getFileContext(String filePath) {
        try {
            ClassPathResource resource = new ClassPathResource(filePath);
            if (!resource.exists()) {
                resource = new ClassPathResource(String.format("config/%s", filePath));
                log.info("配置文件路径:{}", resource.getPath());
                if (!resource.exists()) {
                    resource = new ClassPathResource(String.format("../config/%s", filePath));
                    log.info("配置文件路径:{}", resource.getPath());
                }
            }
            log.info("配置文件路径:{}", resource.getPath());
            InputStream inputStream = resource.getInputStream();
            if (inputStream.available() > 0) {
                byte[] b = new byte[inputStream.available()];
                inputStream.read(b, 0, inputStream.available());
                inputStream.close();
                return new String(b);
            }
            return null;
        } catch (IOException e) {
            log.error("读取配置文件异常{}：", filePath, e);
        }
        return null;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        String templateName = String.format("%stemplate", getIndexPrefix());
        boolean isTemplate = esRestUtils.isTemplateExists(templateName);
        log.info("检查{} 模板...", getIndexPrefix());
        if (!isTemplate) {
            List<String> indexNames = Collections.singletonList(getIndexPrefix() + "*");
            String setting = getLocalSetting();
            String mapping = getLocalMapping();
            //设置别名
            String aliasName = String.format(ALAIS_FORMAT, getIndexPrefix());
            esRestUtils.createTemplate(templateName, indexNames, setting, mapping, aliasName);
            log.info("创建{} 模板...", templateName);
        }
    }

    @Override
    public abstract String getIndexPrefix();

    @Override
    public String getIndexName(String suffix) {
        if (StringUtils.isEmpty(suffix)) {
            return getIndexPrefix();
        }
        return String.format(INDEX_FORMAT, getIndexPrefix(), suffix);
    }

    @Override
    public String getLocalSetting() {
        String filePath = String.format("essetting/%s-Setting.json", getIndexPrefix());
        String json = getFileContext(filePath);
        Assert.hasText(json, String.format("本地%s为空", filePath));
        return json;
    }

    @Override
    public String getLocalMapping() {
        String filePath = String.format("essetting/%s-Mapping.json", getIndexPrefix());
        String json = getFileContext(filePath);
        Assert.hasText(json, String.format("本地%s为空", filePath));
        return json;
    }

    @Override
    public boolean createIndex(String index) throws BusinessException {
        Assert.hasText(index, String.format("%s不能为空", index));
        CreateIndexRequest request = new CreateIndexRequest(index);//创建索引
        //创建的每个索引都可以有与之关联的特定设置。
        String setting = getLocalSetting();
        request.settings(setting, XContentType.JSON);
        String mapping = getLocalMapping();
        //创建索引时创建文档类型映射
        request.mapping(mapping, XContentType.JSON);

        //为索引设置一个别名
        request.alias(
                new Alias(String.format("%s-alias", index))
        );
        //可选参数
        request.setTimeout(TimeValue.timeValueMinutes(2));//超时,等待所有节点被确认(使用TimeValue方式)
        request.setMasterTimeout(TimeValue.timeValueMinutes(1));//连接master节点的超时时间(使用TimeValue方式)
        //request.waitForActiveShards(ActiveShardCount.DEFAULT);
        request.waitForActiveShards(ActiveShardCount.DEFAULT);//在创建索引API返回响应之前等待的活动分片副本的数量，以ActiveShardCount形式表示。
        try {
            //同步执行
            CreateIndexResponse createIndexResponse = esRestUtils.getClient().indices().create(request, RequestOptions.DEFAULT);
            return true;
        } catch (Exception e) {
            log.error("创建index异常{}：", index, e);
            throw new BusinessException("创建index异常");
        }
    }

    @Override
    public boolean insert(String index, String id, String jsonData) throws BusinessException {
        try {
            IndexResponse indexResponse = esRestUtils.insert(index, id, jsonData);
            return true;
        } catch (Exception e) {
            log.error("数据写入Es异常", e);
            throw new BusinessException("数据写入Es异常");
        }
    }

    @Override
    public boolean batchInsert(String index, Map<String, String> valueMap) throws BusinessException {
        BulkResponse bulkRequest = batchInsert(index, valueMap, 0);
        if (null != bulkRequest) {
            int fail_count = 0;
            int create_count = 0;
            for (BulkItemResponse bulkItemResponse : bulkRequest.getItems()) {
                if (bulkItemResponse.status() == RestStatus.CREATED) {
                    create_count++;
                } else {
                    log.debug("es写入数据失败 {}", bulkItemResponse.getFailure().toString());
                    fail_count++;
                }
            }
            log.info("{} 写入{} 失败 {}", index, create_count, fail_count);
            return false;
        }
        return true;
    }

    @Override
    public boolean batchReplace(String index, Map<String, String> valueMap) throws BusinessException {
        try {
            BulkResponse bulkRequest = esRestUtils.insert(index, valueMap);
            return true;
        } catch (Exception e) {
            log.error("数据写入Es异常", e);
            throw new BusinessException("数据写入Es异常");
        }
    }

    @Override
    public boolean update(String index, String id, Map<String, Object> valueMap) throws BusinessException {
        try {
            esRestUtils.update(index, id, valueMap);
            return true;
        } catch (Exception e) {
            log.error("Es数据更新异常", e);
            throw new BusinessException("Es数据更新异常");
        }
    }
    
    @Override
    public boolean delete(String index, String id) throws BusinessException {
        try {
            esRestUtils.delete(index, "_doc", id);
            return true;
        } catch (Exception e) {
            log.error("Es数据删除异常", e);
            throw new BusinessException("Es数据删除异常");
        }
    }
    
    @Override
    public List<Map<String, Object>> getDataById(String[] indexName, String id) throws BusinessException {
    	try {
            return esRestUtils.getDataById(indexName, id);
        } catch (Exception e) {
            log.error("Es根据id查询数据异常", e);
            throw new BusinessException("Es根据id查询数据异常");
        }
    }
    
    @Override
    public List<Map<String, Object>> getDataByIds(String[] indexName, List<String> ids) throws BusinessException {
    	try {
            return esRestUtils.getDataById(indexName, ids);
        } catch (Exception e) {
            log.error("Es根据id集合查询数据异常", e);
            throw new BusinessException("Es根据id集合查询数据异常");
        }
    }
    
    @Override
    public Map<String, Object> page(String[] indexNameArray, QueryBuilder queryBuilder, String fields, int pageNum, int pageSize, SortBuilder sortBuilder) throws BusinessException {
    	try {
    		Map<String, Object> resultMap = new HashMap<String, Object>();
    		resultMap.put("rows", esRestUtils.page(indexNameArray, queryBuilder, fields, pageNum, pageSize, sortBuilder));
    		resultMap.put("total", esRestUtils.getTotalRecords(indexNameArray, queryBuilder).intValue());
            return resultMap;
        } catch (Exception e) {
            log.error("Es分页查询数据列表异常", e);
            throw new BusinessException("Es分页查询数据列表异常");
        }
    }
    
    @Override
    public boolean batchReplaceAndRefresh(String index,Map<String, String> valueMap) throws BusinessException {
    	try {
            BulkResponse bulkRequest = esRestUtils.insertAndRefresh(index, valueMap);
            return true;
        } catch (Exception e) {
            log.error("数据写入Es异常", e);
            throw new BusinessException("数据写入Es异常");
        }
    }
    
    @Override
    public boolean deleteAndRefresh(String index, String id) throws BusinessException {
    	try {
            esRestUtils.deleteAndRefresh(index, "_doc", id);
            return true;
        } catch (Exception e) {
            log.error("Es数据删除异常", e);
            throw new BusinessException("Es数据删除异常");
        }
    }
    
    @Override
    public boolean updateQuery(String index, QueryBuilder queryBuilder, Map<String, Object> params) throws BusinessException {
    	try {
            esRestUtils.updateQuery(index, queryBuilder, params);
            return true;
        } catch (Exception e) {
            log.error("Es根据条件更新异常", e);
            throw new BusinessException("Es根据条件更新异常");
        }
    }
    
    @Override
    public boolean deleteQuery(String index, QueryBuilder queryBuilder) throws BusinessException {
    	try {
            esRestUtils.deleteQuery(index, queryBuilder);
            return true;
        } catch (Exception e) {
            log.error("Es根据条件删除异常", e);
            throw new BusinessException("Es根据条件删除异常");
        }
    }
    
    @Override
    public List<Map<String, Object>> findAll(String indexName, QueryBuilder builder) throws BusinessException {
    	try {
    		return esRestUtils.findAll(indexName, builder);
    	} catch (Exception e) {
            log.error("Es根据条件查询异常", e);
            throw new BusinessException("Es根据条件查询异常");
        }
    }
    
    @Override
    public boolean batchUpdate(String indexName, Map<String, Map<String, Object>> map) throws BusinessException {
    	try {
            esRestUtils.bulkUpdate(indexName, map);
            return true;
        } catch (Exception e) {
            log.error("Es批量更新异常", e);
            throw new BusinessException("Es批量更新异常");
        }
    }
    
}
